/**
 * Copyright @2019 Josin All Rights Reserved.
 * Author: Josin
 * Email : xeapplee@gmail.com
 *
 * This file was used for parsing the http request stream
 * An example for http request stream like this:
 *
 * GET / HTTP/1.1\r\n
 * Code: 200\r\n
 * Connection: keep-alive\r\n
 * \r\n
 */

#ifndef FASTCJSON_FC_HTTP_REQ_H
#define FASTCJSON_FC_HTTP_REQ_H

#include <fc_config.h>
#include <fc_list.h>

#define E_HOST     "Host"
#define E_RANGE    "Range"
#define E_TAG      "ETag"
#define E_CTYPE    "Content-Type"
#define E_KEEP     "keep-alive"
#define E_LENGTH   "Content-Length"
#define E_CONNECT  "Connection"
#define E_HTTP_1_0 "HTTP/1.0"
#define E_HTTP_1_1 "HTTP/1.1"
#define E_HTTP_2_0 "HTTP/2.0"

enum HTTP_HEADER_LINE{ REQUEST_METHOD = 0, REQUEST_URL = 1, REQUEST_HTTP_VERSION = 2 };
enum METHOD_VER_NUM { HTTP_GET = 0, HTTP_PUT = 2, HTTP_POST = 4, HTTP_DELETE = 8, HTTP_HEAD = 16, HTTP_OPTIONS = 32 };
enum HTTP_VER_NUM { HTTP_1_0 = 0, HTTP_1_1 = 2, HTTP_2_0 = 4 };
#define KEEP_ALIVE 1
#define NOT_ALIVE  0
typedef struct __FC_HTTP_REQ {
    char         method_ver_num;
    char         http_ver_num;
    char         keep_alive;
    char        *request_url;
    char        *http_version;
    char        *body;
    char        *range;
    FCL_LIST    *headers;
    char        *code;
    char        *msg;
    unsigned long long  body_len;
    char                method[8];
} FC_HTTP_REQ;

/* FC_HTTP_REQ *NEW_FC_http_req(); */

typedef struct _FC_HTTP_HEADER {
    char *key;
    char *val;
    unsigned long key_len;
    unsigned long val_len;
} FC_HTTP_HEADER;

/*
FC_HTTP_HEADER *NEW_FC_http_header();
void FC_set_http_req(FC_HTTP_REQ *req);
void FC_set_http_header(FC_HTTP_REQ *req, FC_HTTP_HEADER *header);
*/
int TRASH_FC_http_header(void *data);
int TRASH_FC_http_req(void *data);

FC_HTTP_REQ *
FC_http_stream_parse(char *src, unsigned long src_len, int direction, unsigned long *next_pos);

#define FC_PARSE_HTTP_STREAM(str, len) { unsigned long __n = 0, __prev_n; FC_HTTP_REQ *req; do { \
__prev_n = __n;\
req = FC_http_stream_parse(str + __n, len, &__n);\
if ( !req ) {\
    break;\
}\
if (__n == __prev_n) {\
    break;\
}
#define FC_PARSE_HTTP_STREAM_END()\
TRASH_FC_http_req(req);\
} while( TRUE ); }


/**
 * @brief NOTICE
 * Below are some macros for easy get data from FCL_LIST
 */
#define FC_HTTP_HEADER(n) typedef struct __FC_HTTP_HEADER_##n { FC_HTTP_HEADER
#define FC_HTTP_HEADER_END(n) ;int __oth; } CHEADER_##n
#define FC_HTTP_HEADER_FUNC_DEF(n)\
int TRASH_CHEADER_##n(CHEADER_##n *ptr);\
CHEADER_##n *NEW_FC_HTTP_HEADER_##n##_FROM_HEADERS(FCL_LIST *v)

#define FC_HTTP_HEADER_FUNC(n)\
CHEADER_##n *NEW_FC_HTTP_HEADER_##n()\
{\
    CHEADER_##n *ptr = malloc( sizeof(CHEADER_##n ));\
    if ( ptr == NULL ) return NULL;\
    e_memzero(ptr, sizeof(CHEADER_##n));\
    return ptr;\
}\
int TRASH_CHEADER_##n(CHEADER_##n *ptr)\
{\
    if (!ptr) return FALSE;\
    e_memfree(ptr);\
    return TRUE;\
}\
CHEADER_##n *NEW_FC_HTTP_HEADER_##n##_FROM_HEADERS(FCL_LIST *v)\
{\
    FCL_NODE            *node;\
    FC_HTTP_HEADER      *header;\
    CHEADER_##n         *res;\
    if (!v) return NULL;\
    res = NEW_FC_HTTP_HEADER_##n();\
    if ( !res ) return NULL;\
    FCL_LIST_FOREACH_HEAD(v, node) {\
        header = FCL_NODE_DATA_P(node);\
        if (!header) {\
            TRASH_CHEADER_##n(res);\
            return NULL;\
        }
#define FC_HTTP_HEADER_FUNC_END()\
        else {\
            res->__oth = 1;\
        }\
    } FCL_LIST_FOREACH_END();\
    return res;\
}
#define FC_HTTP_HEADER_CMP(name, aname, l, e)\
e ( e_str##l##cmp(header->key, #name) ) {\
    res->aname = header;\
}


/**
 * @brief NOTICE
 * Below are the Simple HTTP header parsing example
 */
FC_HTTP_HEADER(header)
    *contentType, *acceptType, *contentLength
FC_HTTP_HEADER_END(header);

FC_HTTP_HEADER_FUNC_DEF(header);

#endif /* FASTCJSON_FC_HTTP_REQ_H */
